from flask import request, abort, jsonify
from app.models.users import User, RevokedTokenModel
from ext import jwt
from flask_jwt_extended import (create_access_token, create_refresh_token, jwt_required, get_jwt_identity, get_jwt)
from datetime import datetime
from app.utils.code_msg import ResponseCode
from app.utils.response import ResMsg
from . import user_v1_bp

"""
# The following callback functions have all been changed to take two arguments.
# Those arguments are the jwt_headers and jwt_payload.
- @jwt.needs_fresh_token_loader
- @jwt.revoked_token_loader
- @jwt.user_lookup_loader
- @jwt.user_lookup_error_loader
- @jwt.expired_token_loader
- @jwt.token_in_blocklist_loader
- @jwt.token_verification_loader
- @jwt.token_verification_failed_loader
- @jwt.expired_token_loader

参考：https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/#callback-function-changes
"""


@jwt.expired_token_loader
def expired_token_callback(jwt_headers, jwt_payload):
    """
    过期令牌，主要是处理有效但是过期的令牌在访问一个受保护的endpoint之前
    """
    return jsonify(code="401", err="token 已过期"), 401


@jwt.unauthorized_loader
def my_unauthorized_token_callback(jwt_headers):
    """未传头部Authorization"""
    return jsonify(err="Missing Authorization Header"), 401


@jwt.invalid_token_loader
def invalid_token_callback(error):  # we have to keep the argument here, since it's passed in by the caller internally
    """
    无效令牌
    """
    return jsonify({
        'message': 'Signature verification failed.',
        'error': 'invalid_token'
    }), 401


@user_v1_bp.route('/register', methods=['POST'])
def create_user():
    """
    创建用户
    """
    if not request.json or not 'email' in request.json \
            or not 'username' in request.json \
            or not 'password' in request.json:
        abort(400)
    username = request.json['username']
    email = request.json['email']
    password = request.json['password']
    exist_user = User.query.filter_by(username=username).first()
    exist_email = User.query.filter_by(email=email).first()
    if exist_user:
        return jsonify({"msg": "用户已经存在"})
    if exist_email:
        return jsonify({"msg": "email已经存在"})
    user = User(email=email, username=username, password=User.set_password(User, password))
    User.add(User, user)
    return jsonify(user.to_json()), 200


@user_v1_bp.route('/users', methods=['GET'])
# 需要验证token
@jwt_required()
def get_user_list():
    """
    查看所有用户
    """
    user_list = User.query.all()
    return jsonify({"count": len(user_list), 'data': [user.to_json() for user in user_list]}), 200


@user_v1_bp.route('/login', methods=['POST'])
def user_login():
    """
    用户登录
    """
    res = ResMsg()
    if not request.json \
            or not 'username' in request.json \
            or not 'password' in request.json:
        abort(400)
    username = request.json['username']
    password = request.json['password']

    userInfo = User.query.filter_by(username=username).first()
    if (userInfo is None):
        return jsonify({"msg": "用户不存在"})
    else:
        if (User.check_password(User, userInfo.password, password)):
            login_time = str(datetime.now())
            userInfo.login_time = login_time
            User.update(userInfo)
            access_token = create_access_token(identity=username, fresh=True)
            refresh_token = create_refresh_token(identity=username)
            # get_jwt_identity() 可以从token中获取到username，这点在实际项目中非常有用。

            result_msg = dict(access_token=access_token, refresh_token=refresh_token)
            res.update(code=ResponseCode.Success, data=result_msg, msg="登录成功")
            return jsonify(res.data)
        else:
            res.update(code=ResponseCode.Fail, msg="密码不正确")
            return jsonify(res.data)


@user_v1_bp.route('/user/<int:id>', methods=['GET'])
# 需要验证token
@jwt_required()
def get_user(id):
    """
    查看单个用户
    """
    user = User.query.get_or_404(id)
    return jsonify({"data": user.to_json()}), 200


@user_v1_bp.route('/user/<int:id>', methods=['PUT'])
# 需要验证token
@jwt_required()
def update_user(id):
    """
    更新单个用户
    """
    if not request.json:
        abort(400)
    new_user = User.query.get_or_404(id)
    email = request.json['email']
    exist_email = User.query.filter_by(email=email).first()

    if 'username' in request.json:
        username = request.json['username']
        exist_user = User.query.filter_by(username=username).first()
        if exist_user:
            return jsonify({"msg": "用户已经存在"})
        new_user.username = request.json['username']

    if 'email' in request.json:
        email = request.json['email']
        exist_email = User.query.filter_by(email=email).first()
        if exist_email:
            return jsonify({"msg": "email已经存在"})
        new_user.email = request.json['email']

    if 'password' in request.json:
        password = request.json['password']
        # 对新密码进行加密
        new_user.password = User.set_password(User, password)

    if 'is_active' in request.json:
        new_user.is_active = request.json['is_active']

    User.update(new_user)
    return jsonify({'data': new_user.to_json()}), 201


@user_v1_bp.route('/user/<int:id>', methods=['DELETE'])
# 需要验证token
@jwt_required()
def delete_user(id):
    """
    删除单个用户
    """
    if not id:
        abort(400)
    post = User.query.get_or_404(id)
    User.delete(post, id)
    return jsonify({'msg': "删除成功"}), 200


@user_v1_bp.route('/token/refresh', methods=['POST'])
@jwt_required(refresh=True)
def token_refresh():
    """
    刷新access token额外的接口：
    参考:https://www.jianshu.com/p/c155c2b7af42
    """
    current_user = get_jwt_identity()
    new_access_token = create_access_token(identity=current_user)
    return {'access_token': new_access_token}


@jwt.token_in_blocklist_loader
def check_if_token_in_blacklist(jwt_header, decrypted_token):
    """
    回调函数，每次客户端请求被保护的接口时都会调用这个函数，函数要根据token是否在blocklist返回True或False
    """
    jti = decrypted_token['jti']
    return RevokedTokenModel.is_jti_blacklisted(jti)


@user_v1_bp.route('/logout/access', methods=['POST'])
@jwt_required()
def user_logout_access():
    """
    用户登出
    """
    jti = get_jwt()['jti']
    try:
        revoked_token = RevokedTokenModel(jti=jti)
        revoked_token.add()
        return {'message': 'Access token has been revoked'}
    except:
        return {'message': 'Something went wrong'}, 500


@user_v1_bp.route('/logout/refresh', methods=['POST'])
# 需要验证token
@jwt_required(refresh=True)
def user_logout_refresh():
    """
    用户注销
    """
    jti = get_jwt()['jti']
    try:
        revoked_token = RevokedTokenModel(jti=jti)
        revoked_token.add()
        return {'message': 'Refresh token has been revoked'}
    except:
        return {'message': 'Something went wrong'}, 500
